<template>
  <div class="box">
    <div class="video-box">
      <video 
        class="local-video"
        ref="localVideo" 
        src=""
        ></video>
      <video 
        class="remote-video"
        ref="remoteVideo"
        src=""
        ></video>
    </div>
    <div class="btn-box" v-if="!called">
      <button v-if="!caller" @click="callRemote">发起视频</button>
      <button v-else @click="hangUp">挂断视频</button>
    </div>
    <div class="btn-box" v-else>
      <button @click="accpetCall">接受视频请求</button>
      <button @click="hangUp">拒绝视频请求</button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from "vue";
import { FishSocket } from '@/utils/socket';
const called = ref<boolean>(false); // 是否是接受方
const caller = ref<boolean>(false); // 是否是发送方
const calling = ref<boolean>(false); // 呼叫中
const communiccating = ref<boolean>(false); // 视频通话中
const localVideo = ref<HTMLVideoElement>();
const remoteVideo = ref<HTMLVideoElement>();
const roomId = "001";
const socket = ref<FishSocket>();
const peer = ref<RTCPeerConnection>();
let sender: RTCRtpSender;
onMounted(() => {
  
  socket.value = new FishSocket("ws://192.168.2.28:3000/ws");
  
  socket.value.onOpen(() => {
    console.log("连接成功");
    // 加入房间
    socket.value!.sendText("joinRoom", roomId);
  });

  socket.value.onClose(() => {
    console.log("连接关闭");
  });

  // 接收视频请求
  socket.value.on("callRemote", () => {
    // 不是发送方接收
    if (caller.value) return;
      called.value = true;
      calling.value = true;
  });

  // 同意视频请求
  socket.value.on("accpetCall", async () => {
    // 接收方同意了发送方的请求
    if (called.value) return;
    // 开始创建信息
    peer.value = new RTCPeerConnection();
    // 添加本地音视频流生成offer
    const stream = (localVideo.value!.srcObject as MediaStream);
    for (const track of stream.getTracks()) {
      sender = peer.value!.addTrack(track, stream);
    }

    // 通过监听 onicecandidate事件获取candidate信息
    peer.value.onicecandidate = ev => {
      if (ev.candidate) {
        // 向服务器发送 candidate 信息
        socket.value?.sendText("sendCandidate", ev.candidate);
      }
    }

    // 监听onaddstream 获取对方视频流
    peer.value.ontrack = (event) => {
      if (remoteVideo.value?.srcObject !== event.streams[0]) {
        remoteVideo.value!.srcObject = event.streams[0];
        communiccating.value = true;
        calling.value && setTimeout(() => {
          remoteVideo.value!.play();
        },0);
        calling.value = false;
      }
    }

    // 生成offer
    const offer = await peer.value.createOffer({
      offerToReceiveAudio: true,
      offerToReceiveVideo: true,
    });
    // console.log(offer);

    // 本地设置一份 offer 信息
    await peer.value.setLocalDescription(offer);
      
    // 发送offer
    socket.value?.sendText("sendOffer", offer);
    
  });

  // 接收方收到发送方的offer
  socket.value.on("sendOffer", async (offer) => {
    if (caller.value) return
    // console.log("接收offer", offer);
    // 接收者需要创建自己的RTCPeerConnection， 添加本地视频流，设置远端描述，生成answer，并且通过信令服务器进行发送
    peer.value = new RTCPeerConnection();
    // 添加本地视频流
    const stream = (localVideo.value!.srcObject as MediaStream);
    for (const track of stream.getTracks()) {
      sender = peer.value.addTrack(track, stream);
    }

    // 设置远端服务描述
    peer.value.setRemoteDescription(new RTCSessionDescription(offer as RTCSessionDescription));

    // 监听onaddstream 获取对方视频流
    peer.value.ontrack = (event) => {
      if (remoteVideo.value!.srcObject !== event.streams[0]) {
        remoteVideo.value!.srcObject = event.streams[0];
        communiccating.value = true;
        calling.value && setTimeout(() => {
          remoteVideo.value!.play();
        },0);
        calling.value = false;
      }
    }

    // 通过监听 onicecandidate事件获取candidate信息
    peer.value.onicecandidate = (ev) => {
      if (ev.candidate) {
        socket.value?.sendText("sendCandidate", ev.candidate);
      }
    }
   
    // 生成answer
    const answer = await peer.value.createAnswer();
    // 设置本地answer信息
    peer.value.setLocalDescription(answer);
    // 发送 answer
    socket.value?.sendText("sendAnswer", answer);
    
  });

  // 发送方收到接收方的answer
  socket.value?.on("sendAnswer", async (answer) => {
    if (called.value) return;
    // console.log("收到answer：", answer);
    await peer.value?.setRemoteDescription(answer as RTCSessionDescription);
  })

  // 收到 candidate 信息
  socket.value?.on("sendCandidate", async (candidate) => {
    // console.log("收到 candidate", candidate);
    if (peer.value!.remoteDescription) {
      await peer.value!.addIceCandidate(new RTCIceCandidate(candidate as RTCIceCandidateInit));
    }
  });

  socket.value.on("hangUp", () => {
    localVideo.value!.srcObject = null;
    localVideo.value!.pause();
    remoteVideo.value!.srcObject = null;
    remoteVideo.value!.pause();
    peer.value?.removeTrack(sender);
    peer.value?.close();
    communiccating.value = false;
    called.value = false;
    caller.value = false;
  });
});

// 对方发起视频请求
const callRemote = async () => {
  console.log("发起视频");
  // 是否为发起方
  caller.value = true;
  // 是否呼叫中
  calling.value = true;

  // 获取本地视频流
  await getLocalStream();

  // 通过信令服务器像用户b发起请求
  socket.value?.sendText("callRemote", roomId);
}

// 接受方同意视频请求
const accpetCall = async () => {
  // 获取本地视频流
  await getLocalStream();
  socket.value!.sendText("accpetCall", roomId);
}

// 挂断视频
const hangUp = () => {
  console.log("挂断视频");
  socket.value!.sendText("hangUp", roomId);
}

// 获取本地音视频流
const getLocalStream = async () => {
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true
  });

  localVideo.value!.srcObject = stream;
  localVideo.value!.play();

  return stream;
}
</script>

<style scoped lang="scss">
.box {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  
}
.video-box {
  margin: 20px;
  width: 300px;
  height: 500px;
  background-color: #eee;
  position: relative;
  .local-video {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  .remote-video {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 80px;
    height: 120px;
    object-fit: cover;
  }
}
.btn-box {
  display: flex;
  width: 300px;
  justify-content: space-around;
}
</style>